<?php

/**
 * ExtController class file
 *
 * @author TrueSoftware B.V. (The Netherlands)
 * @link http://www.ext4yii.com
 * @copyright Copyright 2007-2010 TrueSoftwareB.V. all rights reserved.
 * @license http://www.ext4yii.com/license/
 *          EVALUATION VERSION - NOT FOR COMMERCIAL USE OR DISTRIBUTION.
 * @version 0.7.2
 */

/**
 * The ExtController is the base class for enabling all Ext4Yii parsing functionality.
 * Your controller must be extended from the ExtController if you wish to have Ext4Yii templates
 * parsed in your views and custom widgets.
 * <p>The ExtController contains many internal member functions which are not viewed on the
 * documentation site. Only the user relevant functions are displayed.</p>
 * <p>Usage:</p>
 * <ext:Code brush="php">
 * <?php
 *      public class MyController extends ExtController
 *      {
 *          public function actionIndex()
 *          {
 *              $this->render('index');
 *          }
 *      }
 * ?>
 * </ext:Code>
 */
class ExtController extends CController
{
    private $_extjs_src = array();
    private $_data_stores = array();
    private $_plugins = array();
    private $_before_init_script = array();
    private $_after_init_script = array();
    private $_e4ypath = null;
    private $_extpath = null;
    private $_uxpath = null;
    private $_resources = null;
    private $_cs = null;
    private $_am = null;
    private $_haspreloader = false;
    private $_ajaxmethods = array();
    private $_global_ids = array();
    private $_ajaxaction = array();
    private $_uxscripts = array();
    private $_uxcss = array();
    private $_ispartial = false;
    public $packages = array();

    public function init()
    {
        parent::init();
        foreach (Yii::app()->components as $component)
        {
            if ($component instanceof Ext4Yii)
            {
                $this->packages = $component->packages;
                break;
            }
        }
    }
    
    /**
     * creates a global id for an ExtJS control. This id will be added to window[id]=object
     * @param ExtWidget $object the parsed Ext4YiiObject
     */
    public function createGlobalID($object)
    {
        $template = '{n:"%s",t:%s}';
        $id = $object->getId();
        if ($object instanceof ExtStore)
            $this->_global_ids[] = sprintf($template, $id, 2);
        else
            $this->_global_ids[] = sprintf($template, $id, 1);
    }

    /**
     * @view
     * ExportToJSONDataSource exports an array to a special Ext4Yii readable JSON object.
     * This method can be used to create JSON data that can be consumed by a ext:Store control.
     * <p>The $total parameter indicates the total number of records. This is useful is case of pagination where
     * the total number of records is needed by the ExtJS control to create the correct pagination<br/>
     * pages (eg. <a target="_blank" href="/api/index.php/site/demo/page/DataControls.GridPanel.PagingToolbar.index/direct/true">ext:PagerToolbar</a>)
     * </p>
     * <p>Usage:</p>
     * <ext:Code brush="php">
     * <?php
     *      $mydata = array(
     *           array('name' => 'John', 'age' => 35),
     *           array('name' => 'Marry','age' => 34)
     *      );
     *      echo $this->ExportToJSONDataSource($mydata);
     * ?>
     * </ext:Code>
     * @param array $data the array to generate a JSON object from it
     * @param int $total total number of records in the entire data-source
     * @return string that represents the JSON notation of the $data parameter
     */
    public function ExportToJSONDataSource($data, $total=null)
    {
        if (is_null($total))
            $total = count($data);

        return sprintf('{counts:%s,rows:%s}', $total, CJSON::encode($data));
    }

    /**
     * Getter for _ajaxmethods
     * @return array
     */
    public function getAjaxMethods()
    {
        return $this->_ajaxmethods;
    }

    /**
     * The addExtJS function registers a raw JavaScript code fragment that is directly inserted inside the Ext.onReady(..) method.
     * This function should not be needed in a normal application development scenario. It is only made available
     * is a rare event when such functionality is required. addExtJS is internally used by Ext4Yii.
     * @param string $data the raw JavaScript code
     */
    public function addExtJS($data)
    {
        $this->_extjs_src[] = $data;
    }

    /**
     * Returns the published resources path
     * @return string
     */
    public function getResourcePath()
    {
        return $this->_resources;
    }

    /**
     * Internal method for processing the AjaxMethod registration and code generation.
     */
    private function processAllAjaxMethods()
    {
        if (Yii::app()->getRequest()->isAjaxRequest)
            return;

        $thisclass = get_class($this);
        $root = Yii::getPathOfAlias("application.controllers");
        $dir = dir($root);
        while (($entry = $dir->read()) != false)
        {
            if (stripos(strtolower($entry), 'controller.php') !== false)
            {
                $class = str_ireplace('.php', '', $entry);
                if (strtolower($thisclass) == strtolower($class))
                {
                    $this->registerAjaxMethods();
                }
                else
                {
                    if (!array_search($class, get_declared_classes()))
                        include($root . '/' . $entry);
                    $controller = new $class($class);
                    if ($controller instanceof ExtController)
                    {
                        $controller->init();
                        $controller->registerAjaxMethods();
                        $this->_ajaxmethods = array_merge($this->_ajaxmethods, $controller->getAjaxMethods());
                        $controller = null;
                    }
                }
            }
        }
        $dir->close();
        $this->prepareAjaxMethods();
    }

    public function renderPartialExtJS($view, $data = null)
    {
        $this->_ispartial = true;
        $this->renderPartial($view, $data, true, true);
    }

    /**
     * This is an internal function.
     * Overrides the parent processOutput to parse the Ext4Yii templates
     * from the PHP parsed document
     * @param string $output must be xhtml document to work properly
     * @return string containing the parsed document
     */
    public function processOutput($output)
    {
        $walker = new ExtASTWalker($output, null, $this);
        $walker->walk();
        foreach ($walker->objects as $item)
            $this->addExtJS($item);


        $this->_data_stores = $walker->renderStores($this->_data_stores);
        $this->_plugins = $walker->renderPlugins($this->_plugins);

        $this->processAllAjaxMethods();

        if ($this->_ispartial)
        {
            $this->renderExtJS();
            return parent::processOutput($walker->getDocument());
        }
        else
        {
            if (is_null($walker->getScriptManager()))
            {
                $this->registerExtScripts();
                $this->renderExtJS();

                return parent::processOutput($walker->getDocument());
            }
            else
            {
                return $this->processWithScriptManager($walker->getScriptManager(),
                        $walker);
            }
        }
    }

    /**
     * Overrides the parent render function for preparing Ext4Yii resources
     */
    public function render($view, $data=null, $return=false)
    {
        $this->prepareResources();
        parent::render($view, $data, $return);
    }

    /**
     * Internal function for handling ExtJS Themes
     * @param ExtScriptManager $sm
     */
    private function processExtJSTheme($sm)
    {
        // process the theme
        $theme = $sm->getTheme();
        if (!is_null($theme) && $theme != '')
        {
            $themename = 'xtheme-' . strtolower($theme) . '.css';
            $fpath = dirname(__FILE__) . '/../../resources/js/ext/bundle/resources/css/' . $themename;
            if (file_exists($fpath))
                $this->addUxCss(array('sm' => true, 'media' => null, 'file' => $this->_extpath . '/bundle/resources/css/' . $themename));
            else
                throw new Exception(Ext4Yii::t('ext4yii', 'The "{theme}" theme is not included in this Ext4Yii package or version.', array('{theme}' => $sm->getTheme())));
        }
    }

    /**
     * Internal function for processing the Ext4Yii JavaScript output in case a pre-loader
     * screen is showed first.
     * @param ExtScriptManager $sm the ext:ScriptManager reference
     * @param ExtASTWalker $walker the parser AST walker
     * @return string is the parsed document output
     */
    private function processWithScriptManager($sm, $walker)
    {
        $this->processExtJSTheme($sm);

        // register script manager provides js files
        if (count($sm->scripts) != 0)
            foreach ($sm->scripts as $script)
                $this->addUxScript($script);

        // register script manager provides css files
        if (count($sm->cssfiles) != 0)
            foreach ($sm->cssfiles as $css)
                $this->addUxCss($css);

        if ($sm->getShowLoader())
        {
            /*
             * we have to re-parse the document where after the document is rendered
             * to insert the loder tags just after the body element. this re-parsing
             * is needed because Yii's way of registering script and css files
             * at the very end.
             */

            $this->_cs->registerCssFile(
                    $this->_resources . '/css/preloading.php?m=' . $sm->getLoaderImage());

            $this->addBeforeInitScript($sm->evaluateValue($sm->getBeforeInitScript()));
            $this->addAfterInitScript($sm->evaluateValue($sm->getAfterInitScript()));

            $this->addAfterInitScript('ext4yii.closePreLoader()');

            $this->_haspreloader = true;
            $this->registerExtScripts(false);
            $this->renderExtJS(false, false);

            $source = parent::processOutput($walker->getDocument());

            $body = null;
            $dom = new ExtHTMLParserDom();
            $dom->load($source, true);

            // find the body tag;
            foreach ($dom->nodes as $node)
                if ($node->nodetype == HDOM_TYPE_ELEMENT && $node->tag == 'body')
                {
                    $body = $node;
                    break;
                }

            if (!is_null($body))
            {
                $sm->setBodyLoadingMask($body);
            }

            return $dom->save();
        }
        else
        {
            $this->addBeforeInitScript($sm->evaluateValue($sm->getBeforeInitScript()));
            $this->addAfterInitScript($sm->evaluateValue($sm->getAfterInitScript()));
            $this->registerExtScripts();
            $this->renderExtJS();
            return parent::processOutput($walker->getDocument());
        }
    }

    /**
     * @view
     * This function can be used to create a return value for an ServerAction event.
     * The return value will be converted to JavaScript in the following forms:
     * <ul>
     *      <li>In case the $result is an associative array, it will be converted into a JSON object.
     *      <li>Regular PHP arrays will be converted to default JavaScript arrays.</li>
     *      <li>PHP boolean values will be converted to JavaScript boolean values.</li>
     *      <li>PHP numeric values will be converted to JavaScript numeric values.</li>
     *      <li>PHP string values will be converted to JavaScript string values.</li>
     *      <li>Implied JavaScript values will be evaluated before the oncomplete handler is called.</li>
     * </ul>
     * The commitAjaxUpdates() method must be called at the very end to send the results to the client.
     * <p>Usage</a>
     * <ext:Code brush="php">
     * class MyController extends ExtController
     * {
     *     public function actionFoo()
     *     {
     *         $this->ServerActionReturn($mydata);
     *         $this->commitAjaxUpdates();
     *     }
     * }
     * </ext:Code>
     * The return value will be automatically appended to the client-side oncomplete event handler's function parameters.
     * <ext:Code brush="js">
     * <ext:ServerAction onevent="click"
     *                  loadmask="true"
     *                  action="/site/controller/action">
     *    <prop:OnCompleteHandler>
     *        <script>
     *            function(sender,event,result)
     *            {
     *               // the "result" parameter contains the
     *               // value of $this->ServerActionReturn(...);
     *            }
     *        </script>
     *    </prop:OnCompleteHandler>
     * </ext:ServerAction>
     * </ext:Code>
     * @param mixed $result the value which will be parsed as JavaScript and sent
     * to the client-side oncomplete event handler as the last function paremeter.
     */
    public function ServerActionReturn($result)
    {
        if(is_array($result))
        {
            $result = sprintf('Ext.decode("%s")',str_replace('"', '\"',CJSON::encode($result)));
            Yii::app()->getController()->addClientAction($result,'s');
        }
        else
        {
            $w = new ExtWidget();
            Yii::app()->getController()->addClientAction(str_replace("\n", '\n',$w->parseValue($result,null,false)),'s');
        }
    }


    /**
     * @view
     * This function retrives the upload Ext4Yii file name
     * @param string $id the ID that is designated to the ext:FileUploadField
     * @return string the name of the uploaded file.
     */
    public function getUploadedFileName($id)
    {
        $fi = $_FILES[$id];
        if (!is_null($fi))
        {
            return $fi['name'];
        }
        else
            return null;
    }

    /**
     * @view
     * This is an specialized version of the PHP move_uploaded_file(..)
     * that is designed to work with Ext4Yii file uploads.
     * This function can be used to move an uploaded file from the PHP temporary upload folder
     * to another location on the server.
     * <p>Usage:</p>
     * <ext:Code>
     * <ext:FileUploadField id="formFile"
     *                      fieldlabel="File"
     *                      allowblank="false">
     * </ext:Code>
     * <ext:Code brush="php">
     * <?php
     *     public function actionProcessFileUpload()
     *     {
     *          $fname = '/tmp/'.$this->getUploadedFileName('formFile');
     *          if($this->moveUploadedFile('formFile', $fname) == UPLOAD_ERR_OK)
     *          {
     *               echo $this->reportFileUploadOK('formFile');
     *               unlink($fname);
     *          }
     *          else
     *          {
     *               echo $this->reportFileUploadError('formFile');
     *          }
     *     }
     * ?>
     * </ext:Code>
     * @param string $id the id that is designated to the ext:FileUploadField
     * @param string $dest the destination folder.
     * @return string/null if there is not errors in moving the uploaded file then the result
     * is a null otherwise a string containing the OS error.
     */
    public function moveUploadedFile($id, $dest)
    {
        $fi = $_FILES[$id];
        if (!is_null($fi) && $fi['error'] == UPLOAD_ERR_OK)
        {
            move_uploaded_file($fi['tmp_name'], $dest);
            return null;
        }
        else
            return $fi['error'];
    }

    /**
     * Internal function for preparing a file upload response JSON
     * @param <type> $id
     * @return <type>
     */
    private function prepareUploadReponse($id)
    {
        $fi = $_FILES[$id];
        $fi['file'] = $fi['name'];
        unset($fi['tmp_name']);
        unset($fi['name']);
        return $fi;
    }

    /**
     * @view
     * This method can be used to report an error when a file upload request
     * cannot be completed successfully.
     * @param string $id the id that is designated to the <b>ext:FileUploadField</b>
     * @param array $extraData any adittional data that is relevant to your file upload process
     * @return string a JSON response string that can be sent to the client
     */
    public function reportFileUploadError($id, $extraData=null)
    {
        $fi = $this->prepareUploadReponse($id);
        $fi['success'] = false;
        switch ($fi['error'])
        {
            case UPLOAD_ERR_OK:
                $fi['errorText'] = Ext4Yii::t('ext4yii', 'There is no error, the file uploaded with success.', array());
                break;
            case UPLOAD_ERR_INI_SIZE:
                $fi['errorText'] = Ext4Yii::t('ext4yii', 'The uploaded file exceeds the upload_max_filesize directive in php.ini.', array());
                break;
            case UPLOAD_ERR_FORM_SIZE:
                $fi['errorText'] = Ext4Yii::t('ext4yii', 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.', array());
                break;
            case UPLOAD_ERR_PARTIAL:
                $fi['errorText'] = Ext4Yii::t('ext4yii', 'The uploaded file was only partially uploaded.', array());
                break;
            case UPLOAD_ERR_NO_FILE:
                $fi['errorText'] = Ext4Yii::t('ext4yii', 'No file was uploaded.', array());
                break;
            case UPLOAD_ERR_NO_TMP_DIR:
                $fi['errorText'] = Ext4Yii::t('ext4yii', 'Missing a temporary folder. Introduced in PHP 4.3.10 and PHP 5.0.3.');
                break;
            case UPLOAD_ERR_CANT_WRITE:
                $fi['errorText'] = Ext4Yii::t('ext4yii', 'Failed to write file to disk. Introduced in PHP 5.1.0.', array());
                break;
            case UPLOAD_ERR_EXTENSION:
                $fi['errorText'] = Ext4Yii::t('ext4yii', 'A PHP extension stopped the file upload. PHP does not provide a way to ascertain which extension caused the file upload to stop; examining the list of loaded extensions with phpinfo() may help. Introduced in PHP 5.2.0.', array());
        }
        $fi['extraData'] = $extraData;
        return CJSON::encode($fi);
    }

    public function reportFileUploadOK($id, $extraData=null)
    {
        $fi = $this->prepareUploadReponse($id);
        $fi['success'] = true;
        $fi['extraData'] = $extraData;
        return CJSON::encode($fi);
    }

    /**
     * @view
     * This function can be used in combination with the <b>ext:PaginToollbar</b>
     * It is a convenience method that retrives the <b>$_POST['start']</b>
     * and <b>$_POST['limit']</b> in an array form.
     * <p>Usage:</p>
     * <ext:Code brush="php">
     * <?php
     *      public function getPagedData()
     *      {
     *          $pinfo = $this->getPagingInfo();
     *          return $this->getSQLData($pinfo['start'],$pinfo['limit']);
     *      }
     * ?>
     * </ext:Code>
     * @return <type>
     */
    public function getPagingInfo()
    {
        return array(
            'start' => intval($_POST['start']),
            'limit' => intval($_POST['limit'])
        );
    }

    /**
     * Internal function for registering init scripts
     * @param <type> $script
     */
    public function addBeforeInitScript($script)
    {
        $this->_before_init_script[] = $script;
    }

    /**
     *
     * @param <type> $script Internal function for registering after init scripts
     */
    public function addAfterInitScript($script)
    {
        $this->_after_init_script[] = $script;
    }

    /**
     * @view
     * This function can be used to register one or more AjaxMethods.
     * The Ext4Yii parser calls this function internally to find the registered
     * AjaxMethods. This function has to be overridden in order to be used correctly.
     * <p>Usage:</p>
     * Please look at the <b>registerAjaxMethod</b> documentation
     */
    public function registerAjaxMethods()
    {

    }

    /**
     * @view
     * This function can be used to register an AjaxMethod. It has to be used in
     * combination with the <b>registerAjaxMethods</b> function.
     * <p>Usage:</p>
     * <ext:Code brush="php">
     * <?php
     *      public class PostgreSQLDatabaseController extends ExtController
     *      {
     *          public function restartDatabaseCluster($ip,$port,$password)
     *          {
     *              // restart the database here
     *          }
     *          public function registerAjaxMethods()
     *          {
     *              $this->registerAjaxMethod('restartDatabaseCluster');
     *          }
     *      }
     * ?>
     * </ext:Code>
     * <p>Demo:</>
     * <a target="_blank" href="/api/index.php/site/demo/page/AjaxMethods.Overview.index/direct/true">Click here..</a>
     * @param string $method the name of the method that is going to available form client-side
     */
    protected function registerAjaxMethod($method)
    {
        $class = get_class($this);
        $id = md5($class . $method);

        $this->_ajaxmethods[$id] = array(
            'controller' => $class,
            'method' => $method
        );
    }

    /**
     * Global action route for handling AjaxMethods requests
     */
    public function actionAjaxmethod()
    {
        $mparams = unserialize($_POST['mparams']);
        $controllerClass = $_POST['controller'];
        $method = $_POST['method'];

        $controller = null;
        if ($this instanceof $controllerClass)
            $controller = $this;
        else
        {
            $controller = new $controllerClass();
            $controller->init();
        }

        $this->addClientAction(call_user_func_array(array($controller, $method), $mparams), 'r');
        $this->commitAjaxUpdates();
    }

    /**
     * Internal function for creating an AjaxMethod's route address
     * @param ExtController $controller the controller containing the AjaxMethod
     * @return string the AjaxMethod's route address
     */
    private function createAjaxMethodRequestUrl($controller)
    {
        $ctrl = strtolower(ucfirst(str_ireplace('controller', '', $controller)) . '/ajaxmethod');
        return Yii::app()->urlManager->createUrl($ctrl);
    }

    /**
     * Internal function for generating the registered Ajax methods
     */
    public function prepareAjaxMethods()
    {
        $minfo = array();
        if (count($this->_ajaxmethods) != 0)
        {
            foreach ($this->_ajaxmethods as $info)
            {
                $controller = $info['controller'];
                $method = $info['method'];
                $ref = new ReflectionClass($controller);
                $list = $ref->getMethods(ReflectionMethod::IS_PUBLIC);
                foreach ($list as $item)
                {
                    if ($item->name == $method && ($ref->name == $controller))
                    {
                        $plist = array();
                        $params = $item->getParameters();
                        foreach ($params as $param)
                            $plist[] = $param->name;

                        $minfo[] = array(
                            'controller' => $controller,
                            'method' => $method,
                            'params' => $plist,
                            'url' => $this->createAjaxMethodRequestUrl($controller)
                        );
                    }
                }
                $ref = null;
                $list = null;
            }

            $id = md5(print_r($minfo, true));
            $this->_ajaxmethods = $id;
            Yii::app()->session->open();
            Yii::app()->session[$id] = $minfo;
            Yii::app()->session->close();
        }
        else
        {
            $this->_ajaxmethods = null;
        }
    }

    /**
     * Internal function for retriving a data store by ID
     * @param string $id the id of the data-source
     * @return ExtWidget the data-source of null of nothing is found
     */
    public function getDataStoreById($id)
    {
        foreach ($this->_data_stores as $key => $store)
            if ($key == $id)
                return $store;
        return null;
    }

    /**
     * Internal function for registered a global datasource (Store)
     * @param ExtWidget $store the datasource to be registered
     */
    public function addDataStore($store)
    {
        $this->_data_stores[$store->getId()] = $store;
    }

    /**
     * Internal function for creating a control plugin;
     * @param ExtWidget $plugin
     */
    public function addPlugin($plugin)
    {
        $this->_plugins[$plugin->getId()] = $plugin;
    }

    /**
     * Internal function that is responsible for rendering the parsed Ext4Yii JavaScript code
     * @param bool $ajaxResponse whether this rendering is based on an Ajax reponse
     * @param int $in_header where to insert the JavaScript segment in the HTML output
     * @return string either the Ajax XML response of the ExtJS JavaScript code
     */
    public function renderExtJS($ajaxResponse=false, $in_header=true)
    {
        if (is_array($this->_extjs_src) || is_array($this->_ajaxaction))
        {
            if (!$ajaxResponse && count($this->_extjs_src) > 0)
            {
                $onready = array();
                $script_pos = $in_header ? CClientScript::POS_HEAD : CClientScript::POS_BEGIN;

                $this->addBeforeInitScript('Ext.QuickTips.init()');

                /* Check GD support */
                if(!Yii::app()->ext4yii->has_gd_support)
                {
                    $message = "Ext.Msg.show({title:'Ext4Yii System Warning',msg: 'It appears that you do not have GD (php_gd2) support enabled for your PHP configuration. Ext4Yii will not be able to render the built-in icons. Please read <a target=\"blank\" href=\"http://www.boutell.com/gd/faq.html\">this</a> article for more information.',buttons: Ext.Msg.OK,icon: Ext.MessageBox.WARNING});";
                    $this->addAfterInitScript($message);
                }

                /* Before Init */
                if (is_array($this->_before_init_script) && count($this->_before_init_script) != 0)
                {
                    $onready = array_merge($onready, $this->_before_init_script);
                }

                /* Store */
                if (is_array($this->_data_stores) && count($this->_data_stores) != 0)
                {
                    $onready = array_merge($onready, $this->_data_stores);
                }

                /* Plugins */
                if (is_array($this->_plugins) && count($this->_plugins) != 0)
                {
                    $onready = array_merge($onready, $this->_plugins);
                }

                /* Controls */
                $onready = array_merge($onready, $this->_extjs_src);

                /* Globals */
                if (is_array($this->_global_ids) && count($this->_global_ids) != 0)
                {
                    $gids = 'ext4yii.registerGlobalIds([' . implode(',', $this->_global_ids) . '])';
                    $onready = array_merge($onready, array($gids));
                }

                /* After Init */
                if (is_array($this->_after_init_script) && count($this->_after_init_script) != 0)
                {
                    $onready = array_merge($onready, $this->_after_init_script);
                }

                $source = implode(';', $onready);

                if ($this->_haspreloader)
                {
                    $delay = trim(Yii::app()->params['ext4yii_preloader_delay']);
                    if (!is_numeric($delay))
                        $delay = 1000;
                    $source = 'Ext.onReady(function(){setTimeout(function(){' . $source . '},' . $delay . ')});';
                }
                else
                {
                    $source = 'Ext.onReady(function(){' . $source . '});';
                }

                if (YII_DEBUG)
                {
                    if (!function_exists('js_beautify'))
                        require_once dirname(__FILE__) . '/jsbeautify.php';
                    $source = js_beautify($source);
                }
                else
                {
                    $source = ExtHelper::packScript($source);
                }

                if (!is_null($this->_cs))
                    $this->_cs->registerScript('ext-app', $source, $script_pos);

                return $source;
            }
            else
            {
                $dom = new DOMDocument();
                $rset = $dom->createElement('rset');
                foreach ($this->_ajaxaction as $item)
                {
                    $rset->appendChild(
                            $this->createResponseValue($dom, $item)
                    );
                }
                $dom->appendChild($rset);
                return $dom->saveXML();
            }
        }
    }

    /**
     * Internal function for creating an Ajax Response Value
     * @param DOMDocument $dom the XML template as DOM object
     * @param array $data data to be inserted in the template
     * @return DOMElement the formated command
     */
    private function createResponseValue($dom, $data)
    {
        $actionType = $data['typ'];
        $valueType = $actionType == 'a' ? 'j' : 'u';
        $value = $data['act'];

        $cmd = $dom->createElement('cmd');
        $cmd->setAttribute('atyp', $actionType);

        if (is_bool($value))
        {
            $value = $value ? 'true' : 'false';
            $valueType = 'bln';
        }
        elseif (is_array($value))
        {
            $value = serialize($value);
            $valueType = 'ary';
        }
        elseif ($value instanceof ExtJavaScriptObject)
        {
            $valueType = 'jso';
            $value = sprintf('ext4yii.JSO(%s)', $value->render());
        }

        $cmd->setAttribute('vtyp', $valueType);
        $act = $dom->createElement('act');
        $act->appendChild($dom->createCDATASection($value));
        $cmd->appendChild($act);
        return $cmd;
    }

    /**
     * @view
     * This function can be used to include an user extended JavaScript file
     * after the ExtJS and Ext4Yii files are inserted in the HTML output.
     * @param string $fname the location of a (published) JavaScript asset.
     */
    public function addUxScript($fname,$package=null)
    {
        if(!is_null($package))
            $this->packages[$package]->addUxScript($fname);
        else
            $this->_uxscripts[] = $fname;
    }

    /**
     * @view
     * This function can be used to include an user extended CSS files
     * after the ExtJS and Ext4Yii files are inserted in the HTML output.
     * @param string $fname the location of a (published) CSS asset.
     */
    public function addUxCss($fname,$package=null)
    {
        if(!is_null($package))
            $this->packages[$package]->addUxCss($fname);
        else
            $this->_uxcss[] = $fname;
    }

    /**
     * This methods registeres the correct i18n script.
     */
    private function register_i18n($script_pos)
    {
        if (Yii::app()->getLanguage() == 'en')
            return;
        $fname = 'ext-lang-' . Yii::app()->getLanguage() . '.js';
        $local = dirname(__FILE__) . '/../../resources/js/ext/bundle/src/locale/' . $fname;
        if (file_exists($local))
        {
            $this->_cs->registerScriptFile($this->_extpath
                    . '/bundle/src/locale/ext-lang-'
                    . Yii::app()->getLanguage()
                    . '.js',
                    $script_pos);
        }
    }

    /**
     * Internal function to prepare the CSS and JavaScript resources that are
     * going to be inserted in the HTML output
     */
    protected function prepareResources()
    {
        if (Yii::app()->getRequest()->isAjaxRequest)
            return;

        $this->_cs = Yii::app()->getClientScript();
        $this->_am = Yii::app()->getAssetManager();
        $this->_resources = $this->_am->publish(dirname(__FILE__) . '/../../resources');
        $this->_extpath = $this->_resources . '/js/ext';
        $this->_e4ypath = $this->_resources . '/js/core';
        $this->_uxpath = $this->_resources . '/js/ux';
        foreach ($this->packages as $package)
        {
            $package->setController($this);
            $package->publishResources();
        }
    }

    /**
     * This method is internally used. Do not call this method directly
     * Registers the necessary extjs javascript and stylesheet files.
     * The current implementation of ExtJS for Yii uses the jquery
     * adapter. Therefore the jquery script is also registered here
     * @return <type>
     */
    public function registerExtScripts($in_header=true)
    {
        if (Yii::app()->getRequest()->isAjaxRequest)
            return;

        $script_pos = $in_header ? CClientScript::POS_HEAD : CClientScript::POS_BEGIN;

        $dotjs = (YII_DEBUG ? '-debug.js' : '.js');

        $this->_cs->registerScriptFile($this->_extpath
                . '/bundle/adapter/ext/ext-base'
                . $dotjs,
                $script_pos);

        $this->_cs->registerScriptFile($this->_extpath
                . '/bundle/ext-all'
                . $dotjs,
                $script_pos);

        $this->register_i18n($script_pos);

        $this->_cs->registerCssFile($this->_resources . '/css/ext4yii.css');

        if(Yii::app()->ext4yii->disable_css_reset)
            $this->_cs->registerCssFile($this->_extpath . '/bundle/resources/css/ext-all-no-reset.css');
        else
            $this->_cs->registerCssFile($this->_extpath . '/bundle/resources/css/ext-all.css');

        $this->_cs->registerScript('ext-blank-img',
                "Ext.BLANK_IMAGE_URL = '"
                . $this->_extpath . "/bundle/resources/images/default/s.gif';",
                $script_pos);

        $this->_cs->registerScriptFile($this->_uxpath . '/jstools.js');
        $this->_cs->registerScriptFile($this->_e4ypath . '/ext4yii' . $dotjs, $script_pos);

        if (!is_null($this->_ajaxmethods))
            $this->_cs->registerScriptFile($this->_e4ypath . '/axmethods.php?a=' . $this->_ajaxmethods
                    , $script_pos);

        if (is_array($this->_uxscripts))
            foreach ($this->_uxscripts as $item)
                if (is_array($item))
                {
                    if (isset($item['sm']))
                        $this->_cs->registerScriptFile($item['file'], $script_pos);
                    else
                        $this->_cs->registerScript(md5($item[0]), $item[0], $script_pos);
                }
                else if (is_file($item))
                {
                    $path = $this->_am->publish($item);
                    $this->_cs->registerScriptFile($path, $script_pos);
                }
                else
                {
                    $this->_cs->registerScriptFile($this->_uxpath . '/' . $item, $script_pos);
                }

        if (is_array($this->_uxcss))
            foreach ($this->_uxcss as $item)
                if (is_array($item))
                {
                    if (isset($item['sm']))
                    {
                        $this->_cs->registerCssFile($item['file'], $item['media']);
                    }
                }
                else
                {
                    $this->_cs->registerCssFile($this->_uxpath . '/' . $item);
                }
    }

    /**
     * Internal function for creating client-side action commands
     * @param string $action what to run
     * @param string $type how to run it
     */
    public function addClientAction($action, $type='a')
    {
        if (is_null($action))
            $action = 'null';
        $this->_ajaxaction[] = array('act' => $action, 'typ' => $type);
    }

    /**
     * @view
     * This function is used in combination with *_OnAjax* methods and has to
     * called at the very end of all *_OnAjax* statements. commitAjaxUpdates is
     * mostly to be used in server-side action handlers. (ext:ServerAction)
     * <p>Usage:</p>
     * <ext:Code brush="php">
     * <?php
     * public class MyController extends ExtController
     * {
     *      public function myServerSideAction()
     *      {
     *          ExtButton::setText('Click Me!','myButton');
     *          $this->commitAjaxUpdates();
     *      }
     * }
     * ?>
     * </ext:Code>
     */
    protected function commitAjaxUpdates()
    {
        header("content-type: text/xml");
        echo $this->renderExtJS(true);
    }

}

?>
